package com.qin.controller;

import java.io.IOException;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.qin.entity.Book;
import com.qin.entity.BookCategory;
import com.qin.entity.Press;
import com.qin.service.BookCategoryService;
import com.qin.service.BookService;
import com.qin.service.impl.BookCategoryServiceImpl;
import com.qin.service.impl.BookServiceImpl;

import cn.hutool.core.util.StrUtil;
import cn.hutool.db.Db;
import cn.hutool.db.Entity;
import cn.hutool.db.handler.BeanHandler;
import cn.hutool.log.StaticLog;

/**
 * 拦截/category下的所有请求
 */
@WebServlet("/category/*")
public class CategoryServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	private BookCategoryService bookCategoryService = new BookCategoryServiceImpl();
	private BookService bookService = new BookServiceImpl();

	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		// 拿到请求的url地址,和请求content路径
		String requestURI = request.getRequestURI();
		String contextPath = request.getContextPath();

		// 去掉*/category/前缀
		String replace = requestURI.replace(contextPath + "/category/", "");

		StaticLog.debug("要请求的分类是：{}", replace);

		// 如果请求路径上带着的id不为空，则根据id转发到指定的页面
		if (!replace.equals("")) {
			// 先转成id
			long categoryId = Long.parseLong(replace);

			// 查出所有的一级分类
			List<BookCategory> categoryByCLevel = bookCategoryService.queryBookCategoryByCLevel(1);
			// 放在请求域中
			request.setAttribute("categoryByCLevel", categoryByCLevel);

			// 根据id查询图书分类
			BookCategory bookCategoryById = bookCategoryService.getBookCategoryById(categoryId);
			request.setAttribute("nowCategoryId", bookCategoryById.getId());
			StaticLog.debug("通过id【{}】查到的图书分类：{}", categoryId, bookCategoryById);

			// 如果没有parent，就代表是1级分类，去book下的index页
			if (bookCategoryById.getParentId() == null) {
				// 根据id设置分类名称
				request.setAttribute("bookCategoryName", bookCategoryById.getName());
				
				// 根据分类id设置二级分类和三级分类
				setLevel1Category(request,response,categoryId);
				
				// 根据分类id设置新书推荐（一个星期内）
				setNewBookCommend(request,response,categoryId);
				
				// 根据分类id设置主编推荐
				setEditorBookCommend(request,response,categoryId);
				
				// 根据分类id设置新书上架（三天内）
				setNewBookPutAwayAndRank(request,response,categoryId);
				
				// 根据分类id设置畅销榜（一个月内）
				setBestSellingAndRank(request,response,categoryId);
				
				// 设置超值（折扣小于5）
				setBestDiscountAndRank(request, response, categoryId);
				
				// 设置热点，点击数大于100
				setHotBookAndRank(request, response, categoryId);
				
				// 转发到对应的页面
				request.getRequestDispatcher("/WEB-INF/book/index.jsp").forward(request, response);
			} else {
				// 拿到bookCategoryById的等级
				Integer catLevel = bookCategoryById.getCatLevel();
				
				if(catLevel==3) {
					// 如果是三级分类,放分类树信息
					setCategoryTreeForLevel3(request,response,bookCategoryById);
					
					// 查询到三级分类下所有的树
					List<Book> booksByCategoryLevel3Id = bookService.getBooksByCategoryLevel3Id(categoryId);
					
					// 设置三级分类下所有的树下的分类信息，主要包括（出版社[bookAuthor]、作者）
					setPressAndAuthorIfNotNull(request,response,booksByCategoryLevel3Id);
					
					// 把查询所有的图书[bookByCategory]和数量[bookNum]并放在请求域中，打乱后（综合排序后）只要前10条数据
					setBookAndNum(request,response,booksByCategoryLevel3Id);
				}else if(catLevel ==2){
					// 如果是二级分类，放分类树的信息，并且还要把三级分类下的所有分类信息并放在请求域中
					setCategoryTreeForLevel2(request,response,bookCategoryById);
					
					// 查询到所有的图书和数量并放在请求域中
					List<Book> booksByCategoryLevel2Id = bookService.getBooksByCategoryLevel2Id(categoryId);
					
					// 设置三级分类下所有的树下的分类信息，主要包括（出版社[bookAuthor]、作者）
					setPressAndAuthorIfNotNull(request,response,booksByCategoryLevel2Id);
					
					// 把查询所有的图书[bookByCategory]和数量[bookNum]放在请求域中，打乱后（综合排序后）只要前10条数据
					setBookAndNum(request,response,booksByCategoryLevel2Id);
				}
				
				// 去book下的book页
				request.getRequestDispatcher("/WEB-INF/book/book.jsp").forward(request, response);
			}
			
		} else {
			// 不然重定向到首页（不做处理）
			response.sendRedirect(contextPath + "/index");
		}
	}

	/**
	 * 设置三级分类下所有的树下的分类信息，主要包括（出版社[bookPress]、作者[bookAuthor]）
	 * @param request
	 * @param response
	 * @param bookByCategoryId 当前分类下的所有图书
	 */
	private void setPressAndAuthorIfNotNull(HttpServletRequest request, HttpServletResponse response,
			List<Book> bookByCategoryId) {
		// 遍历这个图书分类，获取出版社和作者的List
		if(bookByCategoryId != null) {
			// 查出版社
			Set<Press> collect = bookByCategoryId.stream().map(book->{
				Long pressId = book.getPressId();
				// 根据pressId去查询出版社（绝对会查的到）
				Press find = null;
				
				try {
					find = Db.use().find(Entity.create("press").set("id", pressId),new BeanHandler<Press>(Press.class));
					
				} catch (SQLException e) {
					e.printStackTrace();
				}
				
				return find;
			}).collect(Collectors.toSet());
			
			// 查作者（绝对也会有）
			Set<String> collect2 = bookByCategoryId.stream().flatMap(book->{
				String author = book.getAuthor();
				String[] split = StrUtil.trim(author).split(";");
				return Arrays.asList(split).stream();			
			}).collect(Collectors.toSet());
			
			// 放在请求域中
			StaticLog.debug("往请求域中放的出版社：{}和作者：{}", collect,collect2);
			request.setAttribute("bookPress", collect);
			request.setAttribute("bookAuthor", collect2);
		}
		
	}

	
	/**
	 * 查询所有的图书[bookByCategory]和数量[bookNum]和出版社属性[pressName]并放在请求域中，打乱后（综合排序后）只要前50条数据
	 * @param request
	 * @param response
	 * @param bookCategoryById 查询到的图书
	 */
	private void setBookAndNum(HttpServletRequest request, HttpServletResponse response,
			List<Book> bookCategoryById) {
		// 打乱
		Collections.shuffle(bookCategoryById);
		
		// 排序，前十条数据,设置出版社
		List<Book> collect = bookCategoryById.stream().limit(10).map(book->{
			Long pressId = book.getPressId();
			Press find = null;
			try {
				find = Db.use().find(Entity.create("press").set("id", pressId), new BeanHandler<Press>(Press.class));
			} catch (SQLException e) {
				e.printStackTrace();
			}
			book.setPressName(find.getName());
			return book;
		}).collect(Collectors.toList());
		
		request.setAttribute("bookNum", bookCategoryById.size());
		request.setAttribute("bookByCategory", collect);
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doGet(request, response);
	}

	/**
	 * 根据id往请求域中设置:属性名为[bookCategoryLevel2]的二级分类，并为每个二级分类设置其子分类
	 * @param request
	 * @param response
	 * @param categoryId 请求路径中的id
	 * @throws ServletException
	 * @throws IOException
	 */
	public void setLevel1Category(HttpServletRequest request, HttpServletResponse response, Long categoryId)
			throws ServletException, IOException {
			// 把一级分类的子分类查出放在请求域中
			List<BookCategory> bookCategoryLevel2 = bookCategoryService
					.queryBookCategoryByPId(categoryId);
			
			// 如果不为空，设置二级分类的子级分类
			if (bookCategoryLevel2 != null) {
				Iterator<BookCategory> iterator = bookCategoryLevel2.iterator();
				while (iterator.hasNext()) {
					BookCategory bookCategory = iterator.next();

					// 根据id查询他们的子级分类
					List<BookCategory> children = bookCategoryService.queryBookCategoryByPId(bookCategory.getId());

					bookCategory.setChildren(children);
				}
			}
			// 把二级分类设置在请求域中
			request.setAttribute("bookCategoryLevel2", bookCategoryLevel2);
	}

	/**
	 * 往请求域中设置七天以内的3分新书推荐,属性名为[booksInSeven]
	 * @param request
	 * @param response
	 * @param categoryId 分类id
	 * @throws ServletException
	 * @throws IOException
	 */
	public void setNewBookCommend(HttpServletRequest request, HttpServletResponse response, Long categoryId)
			throws ServletException, IOException {
		// 根据一级分类id，查询三级分类下七天内的所有图书：新书推荐
		List<Book> booksInSeven = bookService.getBooksByCategoryLevel1IdAndD(categoryId, 8L);
		
		if (booksInSeven != null) {
			// 先打乱
			Collections.shuffle(booksInSeven);

			// 新书推荐
			List<Book> collect = booksInSeven.stream().limit(3).collect(Collectors.toList());
			request.setAttribute("booksInSeven", collect);
		} else {
			request.setAttribute("booksInSeven", booksInSeven);
		}
	}

	/**
	 * 往请求域中设置主编推荐,要分类id是31（计算机理论）并且只要8条数据,属性名为[editorCommend]
	 * @param request
	 * @param response
	 * @param categoryId 分类id
	 */
	public void setEditorBookCommend(HttpServletRequest request, HttpServletResponse response, Long categoryId) {
		// 先根据一级图书分类得到下面三级分类所有的图书
		List<Book> booksByCategoryLevel1Id = bookService.getBooksByCategoryLevel1Id(categoryId);
		
		if(booksByCategoryLevel1Id != null) {
			// 先打乱
			Collections.shuffle(booksByCategoryLevel1Id);
			
			// 过滤，要分类id是31并且只要8条数据
			List<Book> collect = booksByCategoryLevel1Id.stream().filter(book->{
				return book.getCategoryId() == 31;
			}).limit(8).collect(Collectors.toList());
			
			request.setAttribute("editorCommend", collect);
		}else {
			request.setAttribute("editorCommend", booksByCategoryLevel1Id);
		}
		
		
	}

	/**
	 * 根据分类id设置新书上架[newBookPut]，要十条(三天内的图书)，还有排行榜(十条按照销量)[newBookRank]
	 * @param request
	 * @param response
	 * @param categoryId
	 */
	public void setNewBookPutAwayAndRank(HttpServletRequest request, HttpServletResponse response, Long categoryId) {
		// 先查出所有图书
		List<Book> newBookPut = bookService.getBooksByCategoryLevel1IdAndD(categoryId, 4L);
		if (newBookPut != null) {
			// 先打乱
			Collections.shuffle(newBookPut);

			// 新书推荐
			List<Book> collect = newBookPut.stream().limit(10).collect(Collectors.toList());
			
			// 新书排行榜
			List<Book> collect2 = newBookPut.stream().sorted(Comparator.reverseOrder()).limit(5).collect(Collectors.toList());
			
			request.setAttribute("newBookPut", collect);
			request.setAttribute("newBookRank", collect2);
		
		} else {
			request.setAttribute("newBookPut", newBookPut);
			request.setAttribute("newBookRank", newBookPut);
		}
	
	}
	
	/**
	 * 根据分类id设置畅销[bestSellingBook]和畅销排行榜[bestSellingRank]，要十条(一个月内的图书)，还有排行榜(十条按照销量)
	 * @param request
	 * @param response
	 * @param categoryId
	 */
	public void setBestSellingAndRank(HttpServletRequest request, HttpServletResponse response, Long categoryId) {
		// 先查出所有图书
		List<Book> bestSellingBook = bookService.getBooksByCategoryLevel1IdAndD(categoryId, 31L);
		if (bestSellingBook != null) {
			// 先排除销量小于10的
			 List<Book> collect = bestSellingBook.stream().filter(book->{
				 return book.getSales() > 10;
			 }).collect(Collectors.toList());
			 
			 // 新书排行榜
			 List<Book> collect2 = collect.stream().sorted(Comparator.reverseOrder()).limit(5).collect(Collectors.toList());
			
			 // 打乱
			Collections.shuffle(collect);

			
			request.setAttribute("bestSellingBook", collect);
			request.setAttribute("bestSellingRank", collect2);
		
		} else {
			request.setAttribute("bestSellingBook", bestSellingBook);
			request.setAttribute("bestSellingRank", bestSellingBook);
		}
	}
	
	/**
	 * 根据分类id设置超值[bestDiscountBook]和折扣排行榜[bestDiscountRank]，要十条(一个月内的图书)，还有排行榜(十条按照销量)
	 * @param request
	 * @param response
	 * @param categoryId
	 */
	public void setBestDiscountAndRank(HttpServletRequest request, HttpServletResponse response, Long categoryId) {
		// 先查出所有图书
		List<Book> bestDiscountBook = bookService.getBooksByCategoryLevel1IdAndD(categoryId, 31L);
		if (bestDiscountBook != null) {
			// 先排除折扣大于5的
			 List<Book> collect = bestDiscountBook.stream().filter(book->{
				 return book.getDiscount() <= 5;
			 }).collect(Collectors.toList());
			 
			 // 按照折扣从小到大排序
			 List<Book> collect2 = collect.stream().sorted(Comparator.comparing(Book::getDiscount)).limit(5).collect(Collectors.toList());
			
			 // 打乱
			Collections.shuffle(collect);
			
			request.setAttribute("bestDiscountBook", collect);
			request.setAttribute("bestDiscountRank", collect2);
		
		} else {
			request.setAttribute("bestDiscountBook", bestDiscountBook);
			request.setAttribute("bestDiscountRank", bestDiscountBook);
		}
	}

	/**
	 * 根据分类id设置热点[hotBook]和折扣排行榜[hotRank]，要十条(一个月内的图书)，还有排行榜(十条按照销量)
	 * @param request
	 * @param response
	 * @param categoryId
	 */
	public void setHotBookAndRank(HttpServletRequest request, HttpServletResponse response, Long categoryId) {
		// 先查出所有图书
		List<Book> hotBook = bookService.getBooksByCategoryLevel1IdAndD(categoryId, 31L);
		if (hotBook != null) {
			// 过滤出点击数大于100的图书
			 List<Book> collect = hotBook.stream().filter(book->{
				 return book.getClick() > 100L;
			 }).collect(Collectors.toList());
			 
			 // 按照点击数从大到小排序
			 List<Book> collect2 = collect.stream().sorted(Comparator.comparing(Book::getClick).reversed())
					 .limit(5).collect(Collectors.toList());
						
			request.setAttribute("hotBook", collect);
			request.setAttribute("hotRank", collect2);
		
		} else {
			request.setAttribute("hotBook", hotBook);
			request.setAttribute("hotRank", hotBook);
		}
	}

	/**
	 * 根据二级分类，在请求域中设置一个分类节点树[categoryTree],和他的子分类[childrenCategory]
	 * @param request
	 * @param response
	 * @param bookCategoryById 三级分类
	 */
	private void setCategoryTreeForLevel2(HttpServletRequest request, HttpServletResponse response,
			BookCategory bookCategoryById) {
		// 先查询到图书分类的父分类（1级分类）,根据父分类，查询二级分类的所有分类
		BookCategory bookCategoryLevel1 = bookCategoryService.getBookCategoryById(bookCategoryById.getParentId());
		List<BookCategory> queryBookCategoryByPId = bookCategoryService.queryBookCategoryByPId(bookCategoryLevel1.getId());
		
		// 交换当前分类的位置
		int indexOf = queryBookCategoryByPId.indexOf(bookCategoryById);
		swap(queryBookCategoryByPId, 0, indexOf);
		
		// 设置一级分类的子类
		bookCategoryLevel1.setChildren(queryBookCategoryByPId);
		
		// 放在请求域中
		request.setAttribute("categoryTree", bookCategoryLevel1);
		
		// 放入当前二级分类下的所有子分类信息
		List<BookCategory> queryBookCategoryByPId2 = bookCategoryService.queryBookCategoryByPId(bookCategoryById.getId());
		request.setAttribute("childrenCategory", queryBookCategoryByPId2);
	}
	
	/**
	 * 根据三级分类，在请求域中设置一个分类节点树[categoryTree]
	 * @param request
	 * @param response
	 * @param bookCategoryById 三级分类
	 */
	public void setCategoryTreeForLevel3(HttpServletRequest request, HttpServletResponse response, BookCategory bookCategoryById) {
		// 不然就是三级分类，查到他父级分类，也就是二级分类
		BookCategory bookCategoryLevel2 = bookCategoryService.getBookCategoryById(bookCategoryById.getParentId());
		
		// 根据二级分类的parentId查找他的父分类，也就是一级分类
		BookCategory bookCategoryLevel1 = bookCategoryService.getBookCategoryById(bookCategoryLevel2.getParentId());
		
		// 查找一级分类的子分类，让当前二级分类在List中排行第一
		List<BookCategory> children1 = bookCategoryService.queryBookCategoryByPId(bookCategoryLevel1.getId());
		int indexOf1 = children1.indexOf(bookCategoryLevel2);
		swap(children1,0,indexOf1);
		
		
		// 查找二级分类的子分类,让当前三级分类在List中排行第一
		List<BookCategory> children2 = bookCategoryService.queryBookCategoryByPId(bookCategoryLevel2.getId());
		int indexOf2 = children2.indexOf(bookCategoryById);
		swap(children2,0,indexOf2);
		
		// 先设置二级分类的子分类
		bookCategoryLevel2.setChildren(children2);
		// 再重新设置一级分类的子分类的第一个元素
		children1.set(0, bookCategoryLevel2);
		// 最后设置一级分类的子分类
		bookCategoryLevel1.setChildren(children1);
		
		// 把这个分类继承树放在session中
		request.setAttribute("categoryTree", bookCategoryLevel1);
	}
	
	/**
	 * 交换List中两个下标下的元素
	 * @param <E>
	 * @param list
	 * @param index1
	 * @param index2
	 */
	private static <E> void swap(List<E> list,int index1,int index2) {
		//定义第三方变量
		E e=list.get(index1);
		//交换值
		list.set(index1, list.get(index2));
		list.set(index2, e);
	}
}
